package org.vectrics.recruiter.job;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;

import org.apache.log4j.Logger;
import org.vectrics.NoProviderException;
import org.vectrics.SystemException;
import org.vectrics.common.db.Auditable;
import org.vectrics.common.db.DataUtils;
import org.vectrics.common.db.ExpirableStartDateComparator;
import org.vectrics.common.db.OwnedObject;
import org.vectrics.common.db.RecordObject;
import org.vectrics.common.util.DateSync;
import org.vectrics.company.Company;
import org.vectrics.company.SalaryType;
import org.vectrics.domain.Domain;
import org.vectrics.location.Location;
import org.vectrics.location.LocationService;
import org.vectrics.provider.CoreServiceLocator;
import org.vectrics.recruiter.RecruitServiceLocator;
import org.vectrics.recruiter.user.CandidateInfo;
import org.vectrics.skill.Skill;
import org.vectrics.skill.SkillRating;
import org.vectrics.user.Person;

public class Job implements RecordObject, Serializable, Auditable, OwnedObject  {
	
	private static final long serialVersionUID = 3977422091134409921L;

	public static final String MONTHLY = "monthly";
	
	public static String EVENT_CREATED = "job.created";	
	public static String EVENT_STATUS_CHANGE = "job.sts.chg";	
	public static String EVENT_CHANGE = "job.chg";
	
	public static String OBJECT_TYPE = "recruit-job";
	
    public static String SCHEDULE_ANY = "any";
    public static String SCHEDULE_PARTTIME = "parttime";
    public static String SCHEDULE_FULLTIME = "fulltime";
    
    public static String SHIFT_ANY = "any";
    public static String SHIFT_DAY = "day";
    public static String SHIFT_NIGHT = "night";
    
    public static String PAY_SALARIED = "salry";
    public static String PAY_HOURLY = "hourly";
    
    public static String SALARY_ANNUAL = "annual";
    public static String SALARY_HOURLY = "hourly";
    public static String SALARY_MONTHLY = "monthly";    
	
	private transient static Logger log = Logger.getLogger(Job.class);
	private Job parent = null;
	private Collection children = null;
	private Long id = null;
	private String title = null;
	private String summary =null;
	private String referenceId = null;
    private JobTerm jobTerm = null;
	private String workSchedule = Job.SCHEDULE_FULLTIME;
	private String shift = Job.SHIFT_DAY;
	private Long travelPercent = null;
	private Long count = new Long(1);
	private JobFunction function = null;
	private Location location = null;
	private Set statusList = new HashSet();
	private Company company = null;
	private String companyInfo = null;
	private String requiredSkills = null;
	private Date dateLastSkillRating = null;
	//private Date dateLastChanged = new Date();
	private boolean hideSalary = false;
	private BigDecimal salary = null;
	private BigDecimal salaryMax = null;
	private String description;
	private String education;
	private Integer duration;
	public static String NOT_GIVEN = "Not Specified";
	private Set jobCandidates = new HashSet();
	private String searchWords = null;
	private boolean voided = false;
	private Person primaryRecruiter = null;
	private Person secondaryRecruiter = null;
	private Person sourcePerson = null;
	private Person hiringManager = null;
	private Company sourceCompany = null;
	private JobPayType payType;
	private SalaryType salaryType;
	private Person createdBy = null;
	private Date createdDate = null;
	private Person lastModifiedBy = null;
	private Date lastModifiedDate = null;
	private Date datePosted = null;	
	private Domain domain = null;
	private JobClassification classification = null;
	private Person requester = null;
	/**
	 * Cantidad de personas buscadas para el job
	 */
	private Integer numberOfPositions = null;
	
	/**
	 * Cantidad de cv que se presentaron en la busqueda
	 */
    private Integer numberOfReceivedResumes = 0;
	
	/**
     * @return Returns the primaryRecruiter.
     */
    public Person getPrimaryRecruiter() {
        return primaryRecruiter;
    }
    /**
     * @param primaryRecruiter The primaryRecruiter to set.
     */
    public void setPrimaryRecruiter(Person primaryRecruiter) {
        this.primaryRecruiter = primaryRecruiter;
    }
    /**
     * @return Returns the secondaryRecruiter.
     */
    public Person getSecondaryRecruiter() {
        return secondaryRecruiter;
    }
    /**
     * @param secondaryRecruiter The secondaryRecruiter to set.
     */
    public void setSecondaryRecruiter(Person secondaryRecruiter) {
        this.secondaryRecruiter = secondaryRecruiter;
    }
	/**
	 */
	public Job() {
		super();
	}

	public String getLocationDescription() {
	    String desc = "Not Given";
	    if (this.getLocation() != null)
	        desc = this.getLocation().getName();
	    return(desc);
	}
	
	private LocationService getLocationProvider() throws NoProviderException {
	    LocationService prov = CoreServiceLocator.getLocationService();
		return(prov);
	}
	
	/**
	 * @return Returns the salary.
	 */
	public BigDecimal getSalary() {
		return salary;
	}
	/**
	 * @param salary The salary to set.
	 */
	public void setSalary(BigDecimal salary) {
		this.salary = salary;
	}
	
    public JobCandidate addJobCandidate(CandidateInfo candidateInfo) {
		JobCandidate jobCandidate = null;
    	if (this.findJobCandidate(candidateInfo) == null) {
    		jobCandidate = new JobCandidate();
    		jobCandidate.setJob(this);
    		jobCandidate.setCandidate(candidateInfo);
    		this.jobCandidates.add(jobCandidate);
    	} else {
    		throw new SystemException("Person is already a job candidate");
    	}
    	return(jobCandidate);
	}
	
	public void copy(Job job) {
		parent = job.getParent();
		title = job.getTitle();
		summary = job.getSummary();
		referenceId = job.getReferenceId();
		jobTerm = job.getJobTerm();
		workSchedule = job.getWorkSchedule();
		travelPercent = job.getTravelPercent();
		statusList = job.getStatusList();
		function = job.getFunction();
		location = job.getLocation();
		company = job.getCompany();
		companyInfo = job.getCompanyInfo();
		requiredSkills = job.getRequiredSkills();
		hiringManager = job.getHiringManager();
		salary = job.getSalary();
		description = job.getDescription();
		searchWords = job.getSearchWords();
	}

	public SkillRating findSkillRating(Skill skill) {
		SkillRating rating = null;
		return(rating);
	}
	
	public Set findAllSkillRatings() {
		Set ratings = null;
		return(ratings);
	}
	
    public JobStatus getCurrentStatus() {
    	JobStatus currentStatus = null;
    	if (statusList != null) {
	    	Iterator iter = this.statusList.iterator();
	    	while (iter.hasNext()) {
	    		JobStatus status = (JobStatus)iter.next();
	    		if (status.getVoided() == false) {
	    			if (status.getDateEnd() == null) {
	    				currentStatus = status;
	    			}
	    		}
	    	}
    	}
    	return(currentStatus);
    }
    
    public JobStatus setStatus(String statusTypeCode, Person authPerson, JobStatusReasonType reasonType) {
    	JobStatusType statusType = RecruitServiceLocator.getJobProvider().findJobStatusType(statusTypeCode, this.getDomain());
    	return(setStatus(statusType, authPerson, reasonType));
    }
    
    public JobStatus setStatus(JobStatusType statusType, Person authPerson) {
    	JobStatusReasonType reason = null; 
    	JobStatus previousStatus = this.getCurrentStatus();
    	if (previousStatus == null) {
        	reason = RecruitServiceLocator.getJobProvider().findDefaultStatusReason(this.getDomain(), 
        			null, statusType.getCode());
    	} else {
        	reason = RecruitServiceLocator.getJobProvider().findDefaultStatusReason(this.getDomain(), 
        			this.getCurrentStatus().getStatusType().getCode(), statusType.getCode());
    	}
    	return this.setStatus(statusType, authPerson, reason);
    }
    
    public JobStatus setStatus(JobStatusType statusType, Person authPerson, JobStatusReasonType reasonType) {
    	JobStatus status = new JobStatus();
		JobStatus currentStatus = this.getCurrentStatus();
		if ((currentStatus == null) || (!currentStatus.getStatusType().getCode().equals(statusType.getCode()))) {
	    	Date now = DateSync.getCurrentTime();
	    	expireCurrentStatus(now);
	    	status = new JobStatus();
	    	status.setDateStart(now);
	    	status.setDateEnd(null);
	    	status.setPerson(authPerson);
	    	status.setStatusType(statusType);
	    	status.setStatusReasonType(reasonType);
	    	this.statusList.add(status);
		} else {
			status = currentStatus;
		}
    	status.setJob(this);
		
		log.debug("If job is posted, post date becomes now.");
		if (status.getStatusType().getCode().equals(JobStatusType.STATUS_POSTED)) {
			this.setDatePosted(DateSync.getCurrentTime());
		}
		
		log.debug("If job is unposted, clear the data posted from the record");
		if (status.getStatusType().getCode().equals(JobStatusType.STATUS_UNPOSTED)) {
			this.setDatePosted(null);
		}
    	return(status);
    }
    
    public JobStatus getPreviousStatus(JobStatus fromStatus) {
    	JobStatus previousStatus = null;
    	TreeSet sortedStatusList = new TreeSet(new ExpirableStartDateComparator());
    	sortedStatusList.addAll(DataUtils.removeVoided(this.statusList));
    	boolean done = false;
    	Iterator iter = sortedStatusList.iterator();
    	while ((iter.hasNext()) && (done == false)) {
    		 JobStatus currentStatus = (JobStatus)iter.next();
    		 if (currentStatus.getId().equals(fromStatus)) {
    		 	done = true;
    		 } else {
    		 	previousStatus = currentStatus;
    		 }
    	}
    	return(previousStatus);
    }
    
	private void expireCurrentStatus(Date expireDate) {
		JobStatus status = this.getCurrentStatus();
		if (status != null) {
			status.setDateEnd(expireDate);
		}
	}
    
    public Set getStatusList() {
    	return(this.statusList);
    }
    
    protected void setStatusList(Set statusList) {
    	this.statusList = statusList;
    }

    /**
     * @return Returns the count.
     */
    public Long getCount() {
        return count;
    }
    /**
     * @param count The count to set.
     */
    public void setCount(Long count) {
        this.count = count;
    }
	public void removeSkillRating(Skill skill) {
		//TODO
	}
	
	public SkillRating createSkillRating(Skill skill) {
		SkillRating skillRating = null;
		
		//TODO
		return(skillRating);
	}
	
	public void clearAllSkillRatings() {
		//TODO
		
	}
	
	public Long getId() {
		return this.id;
	}

	public void setId(Long id) {
		this.id = id;
	}

    
	public String getTitle() {
		return(title);
	}
	
	public void setTitle(String title) {
		this.title = title;
	}
    
	public String getSummary() {
		return(this.summary);
	}
	
	public void setSummary(String summary) {
		this.summary = summary;
	}
    
	public String getReferenceId() {
		return(this.referenceId);
	}
	
	public void setReferenceId(String refId) {
		this.referenceId = refId;
	}
    
	public String getTermCode() {
		if (this.getJobTerm() == null) {
			return "";
		}
		return this.getJobTerm().getCode();
	}   
    
	public String getWorkSchedule() {
		return(workSchedule);
	}
	
	public void setWorkSchedule(String value) {
		this.workSchedule = value;
	}
	
	public String getWorkScheduleDescription() {
		String desc = NOT_GIVEN;
		if (this.getWorkSchedule() == null) {
			log.debug("doRender() - none");
		}
		else if (this.getWorkSchedule().equals(Job.SCHEDULE_FULLTIME)) {
			log.debug("doRender() - fulltime");
			desc = "Full-Time";
		}
		else if (this.getWorkSchedule().equals(Job.SCHEDULE_PARTTIME)) {
			log.debug("doRender() - parttime");
			desc = "Part-Time";
		}
		else  {
			log.debug("doRender() - full and part time");
			desc = "Full-Time or Part-Time";
		}
		return(desc);
	}
    
	public String getTermDescription() {
		if (this.getJobTerm() == null) {
			return "";
		}
		return(this.getJobTerm().getDescription());
	}
	
	public Long getTravelPercent() {
		return(travelPercent);
	}
	
	public void setTravelPercent(Long value) {
		this.travelPercent = value;
	}
    
    
    
    
    /**
     * @return Returns the hiringManager.
     */
    public Person getHiringManager() {
        return hiringManager;
    }
    /**
     * @param hiringManager The hiringManager to set.
     */
    public void setHiringManager(Person hiringManager) {
        this.hiringManager = hiringManager;
    }

	public String getFunctionDescription() {
		String desc = NOT_GIVEN;
		if (this.getFunction() != null) {
			desc = this.getFunction().getDescription();
		}
		return(desc);
	}

    
    
	public Set getLocations() {
		Set locations = null;
		//TODO
		return(locations);
	}
	
	public void clearLocations() {
		//TODO
	}
    
	public String getDescription() {
		return(this.description);
	}
	
	public void setDescription(String description) {
		this.description = description;
	}
    
	public String getRequiredSkills() {
		return(this.requiredSkills);
	}

	public void setRequiredSkills(String str) {
		this.requiredSkills = str;
	}
	

	public String getCompanyInfo() {
		return(this.companyInfo);
	}

	public void setCompanyInfo(String str) {
		this.companyInfo = str;
	}

	
    
	/**
	 * @hibernate.property
	 *  column="featured"
	 *  not-null="true"
	 */
	public boolean getFeatured() {
		return(this.featured);
	}
	public void setFeatured(boolean value) {
		featured = value;
	}
	
	boolean featured = false;

	/**
	 * @hibernate.property
	 *  column="telecommute"
	 *  not-null="true"
	 */
	public boolean getTelecommute() {
		return(this.telecommute);
	}
	public void setTelecommute(boolean value) {
		telecommute = value;
	}
	private boolean telecommute = false;
    
    
	
	
	public void createTitleSearchWords() {
		//TODO
	}
	
 	public void clearSkillSet() {
		// TODO
		
	}

 	
 	
    /**
     * @param jobCandidates The jobCandidates to set.
     */
    public void setJobCandidates(Set jobCandidates) {
        this.jobCandidates = jobCandidates;
    }
	public Set getJobCandidates() {
		log.debug("getJobCandidates()");
		return(jobCandidates);
	}
	

	public void clearJobCandidates() {
		log.debug("clearJobCandidates()");
		Iterator iter = this.getJobCandidates().iterator();
		while (iter.hasNext()) {
			JobCandidate jobCandidate = (JobCandidate)iter.next();
			jobCandidate.setVoided(true);
		}
	}
	

	public void addCandidatesWithSkills(int minimumScore) {
		//TODO
	}
	
	public JobCandidate findJobCandidate(CandidateInfo candidateInfo) {
		log.debug("Job.findJobCandidate() called");
		if (candidateInfo == null)
			throw new SystemException("findJobCandidate(candidateInfo) was passed a null value");
		JobCandidate found = null;
		Iterator iter = getJobCandidates().iterator();
		while ((iter.hasNext()) && (found == null)) {
			JobCandidate candidate = (JobCandidate)iter.next();
			if (candidate.getCandidate() != null) {
			    if (candidate.getCandidate().getId() != null) {
					if (candidate.getCandidate().getId().equals(candidateInfo.getId())) {
						found = candidate;
					}
			    } else {
			        if (candidate.getCandidate() == candidateInfo)
			            found = candidate;
			    }
			} else {
			    throw new SystemException("Found job candidate with null candidate");
			}
		}
		return(found);
	}

	/**
	 * @param searchWords The searchWords to set.
	 */
	public void setSearchWords(String searchWords) {
		this.searchWords = searchWords;
	}

	/**
	 * @return Returns the searchWords.
	 */
	public String getSearchWords() {
		return searchWords;
	}

	/**
	 * @return Returns the location.
	 */
	public Location getLocation() {
		return location;
	}

	/**
	 * @param location The location to set.
	 */
	public void setLocation(Location location) {
		this.location = location;
	}

	/**
	 * @return Returns the voided.
	 */
	public boolean getVoided() {
		return voided;
	}

	/**
	 * @param voided The voided to set.
	 */
	public void setVoided(boolean voided) {
		this.voided = voided;
	}

	/**
	 * @return Returns the function.
	 */
	public JobFunction getFunction() {
		return function;
	}

	/**
	 * @param function The function to set.
	 */
	public void setFunction(JobFunction function) {
		this.function = function;
	}

	/**
	 * @return Returns the company.
	 */
	public Company getCompany() {
		return company;
	}

	/**
	 * @param company The company to set.
	 */
	public void setCompany(Company company) {
		this.company = company;
	}
	
	
	public void removeJobCandidate(Person user) {
	    JobCandidate removeJobCandidate = null;
	    if (getJobCandidates() != null) {
	        Iterator iter = this.getJobCandidates().iterator();
	        while (iter.hasNext()) {
	            JobCandidate jobCandidate = (JobCandidate)iter.next();
	            if (jobCandidate.getCandidate().getPerson().getId().equals(user.getId())) {
	                removeJobCandidate = jobCandidate;
	            }
	        }
	    }
	    if (removeJobCandidate != null)
	        getJobCandidates().remove(removeJobCandidate);
	}
		
	
    /**
     * @return Returns the sourceCompany.
     */
    public Company getSourceCompany() {
        return sourceCompany;
    }
    /**
     * @param sourceCompany The sourceCompany to set.
     */
    public void setSourceCompany(Company sourceCompany) {
        this.sourceCompany = sourceCompany;
    }
    /**
     * @return Returns the sourcePerson.
     */
    public Person getSourcePerson() {
        return sourcePerson;
    }
    /**
     * @param sourcePerson The sourcePerson to set.
     */
    public void setSourcePerson(Person sourcePerson) {
        this.sourcePerson = sourcePerson;
    }
	
    /**
     * @return Returns the duration.
     */
    public Integer getDuration() {
        return duration;
    }
    /**
     * @param duration The duration to set.
     */
    public void setDuration(Integer duration) {
        this.duration = duration;
    }
    /**
     * @return Returns the education.
     */
    public String getEducation() {
        return education;
    }
    /**
     * @param education The education to set.
     */
    public void setEducation(String education) {
        this.education = education;
    }
    
	/**
	 * @return Returns the shift.
	 */
	public String getShift() {
		return shift;
	}
	/**
	 * @param shift The shift to set.
	 */
	public void setShift(String shift) {
		this.shift = shift;
	}
	
	
	/**
	 * @return Returns the children.
	 */
	public Collection getChildren() {
		return children;
	}
	/**
	 * @param children The children to set.
	 */
	public void setChildren(Collection children) {
		this.children = children;
	}
	/**
	 * @return Returns the parent.
	 */
	public Job getParent() {
		return parent;
	}
	/**
	 * @param parent The parent to set.
	 */
	public void setParent(Job parent) {
		this.parent = parent;
	}
	
	public boolean getActive() {
		return(this.getCurrentStatus().getStatusType().getCode().equals(JobStatusType.STATUS_POSTED));
	}
	
	/**
	 * @return Returns the payType.
	 */
	public JobPayType getPayType() {
		return payType;
	}
	/**
	 * @param payType The payType to set.
	 */
	public void setPayType(JobPayType payType) {
		this.payType = payType;
	}
	/**
	 * @return Returns the salaryType.
	 */
	public SalaryType getSalaryType() {
		return salaryType;
	}
	/**
	 * @param salaryType The salaryType to set.
	 */
	public void setSalaryType(SalaryType salaryType) {
		this.salaryType = salaryType;
	}

	/**
	 * @return Returns the salaryMax.
	 */
	public BigDecimal getSalaryMax() {
		return salaryMax;
	}
	/**
	 * @param salaryMax The salaryMax to set.
	 */
	public void setSalaryMax(BigDecimal salaryMax) {
		this.salaryMax = salaryMax;
	}

	
	public Collection getHiredPersons() {
		Collection hiredPersons = new HashSet();
		Iterator iter = this.getJobCandidates().iterator();
		while (iter.hasNext()) {
			JobCandidate jobCandidate = (JobCandidate)iter.next();
			JobCandidateCompanyPersonRole role = jobCandidate.getJobCandidateCompanyPersonRole();
			if (role != null) {
				hiredPersons.add(jobCandidate.getCandidate());
			}
		}
		return(hiredPersons);
	}

	/**
	 * @return Returns the dateLastSkillRating.
	 */
	public Date getDateLastSkillRating() {
		return dateLastSkillRating;
	}
	/**
	 * @param dateLastSkillRating The dateLastSkillRating to set.
	 */
	public void setDateLastSkillRating(Date dateLastSkillRating) {
		this.dateLastSkillRating = dateLastSkillRating;
	}
	/**
	 * @return Returns the createdBy.
	 */
	public Person getCreatedBy() {
		return createdBy;
	}
	/**
	 * @param createdBy The createdBy to set.
	 */
	public void setCreatedBy(Person createdBy) {
		this.createdBy = createdBy;
	}
	/**
	 * @return Returns the createdDate.
	 */
	public Date getCreatedDate() {
		return createdDate;
	}
	/**
	 * @param createdDate The createdDate to set.
	 */
	public void setCreatedDate(Date createdDate) {
		this.createdDate = createdDate;
	}
	/**
	 * @return Returns the lastModifiedBy.
	 */
	public Person getLastModifiedBy() {
		return lastModifiedBy;
	}
	/**
	 * @param lastModifiedBy The lastModifiedBy to set.
	 */
	public void setLastModifiedBy(Person lastModifiedBy) {
		this.lastModifiedBy = lastModifiedBy;
	}
	/**
	 * @return Returns the lastModifiedDate.
	 */
	public Date getLastModifiedDate() {
		return lastModifiedDate;
	}
	/**
	 * @param lastModifiedDate The lastModifiedDate to set.
	 */
	public void setLastModifiedDate(Date lastModifiedDate) {
		this.lastModifiedDate = lastModifiedDate;
	}
	
	public String toString() {
		if (this.getId() == null)
			return("null");
		return(this.getId().toString());
	}

	public boolean logEntityChanges() {
		return(true);
	}

	public Date getDateFilled() {
		Date dateFilled = null;
		Iterator iter = this.getStatusList().iterator();
		while (iter.hasNext()) {
			JobStatus jobStatus = (JobStatus)iter.next();
			if (jobStatus.getStatusType().getCode().equals(JobStatusType.STATUS_FILLED)) {
				dateFilled = jobStatus.getDateStart();
			}
		}
		return(dateFilled);
	}
	
	/**
	 * @return Returns the displayedPostedDate.
	 */
	public Date getDatePosted() {
		return this.datePosted;
	}
	/**
	 * @param displayedPostedDate The displayedPostedDate to set.
	 */
	protected void setDatePosted(Date date) {
		this.datePosted = date;
	}

	
	
	public JobClassification getClassification() {
		return classification;
	}
	public void setClassification(JobClassification classification) {
		this.classification = classification;
	}
	/**
	 * @return Returns the domain.
	 */
	public Domain getDomain() {
		return domain;
	}
	/**
	 * @param domain The domain to set.
	 */
	public void setDomain(Domain domain) {
		this.domain = domain;
	}
	
	public boolean getHideSalary() {
		return hideSalary;
	}
	public void setHideSalary(boolean hideSalary) {
		this.hideSalary = hideSalary;
	}
	/**
	 * @return the requester
	 */
	public Person getRequester() {
		return requester;
	}
	/**
	 * @param requester the requester to set
	 */
	public void setRequester(Person requester) {
		this.requester = requester;
	}
	/**
	 * @return the jobTerm
	 */
	public JobTerm getJobTerm() {
		return jobTerm;
	}
	/**
	 * @param jobTerm the jobTerm to set
	 */
	public void setJobTerm(JobTerm jobTerm) {
		this.jobTerm = jobTerm;
	}
	
	/**
	 * Obtiene la cantidad de personas buscadas en el job
	 * @param numberOfPositions
	 */
	public Integer getNumberOfPositions() {
		return numberOfPositions;
	}
	
	/**
	 * Setea la cantidad de personas buscadas en el job
	 * @param numberOfPositions
	 */
	public void setNumberOfPositions(Integer numberOfPositions) {
			this.numberOfPositions = numberOfPositions;
	}
	
	/**
	 * @return the numberOfReceivedResumes
	 */
	public Integer getNumberOfReceivedResumes() {
		return numberOfReceivedResumes;
	}
	
	/**
	 * @param numberOfReceivedResumes the numberOfReceivedResumes to set
	 */
	public void setNumberOfReceivedResumes(Integer numberOfReceivedResumes) {
		this.numberOfReceivedResumes = numberOfReceivedResumes;
	}

	
}
